﻿using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

// 游戏
public class Game: MonoBehaviour {
    public class GameOverEvent: UnityEvent<bool> {}    // 游戏结束事件
    public class GotoLevelEvent: UnityEvent<int> {}    // 进入关卡事件
    public class ManMovedEvent: UnityEvent<int> {}     // 小人移动事件
    
    // 移动结点
    private class MoveNode {
        public BaseGameObject Box { get; }  // 被推动的箱子
        public KeyCode Direction { get; }   // 移动方向

        /**
         * 构造方法。
         * @param box       被推动的箱子
         * @param direction 移动方向
         */
        public MoveNode(BaseGameObject box, KeyCode direction) {
            Box = box;
            Direction = direction;
        }
    }

    // 状态
    public enum Status {
        END,    // 已结束
        PLAYING // 游戏中
    }

    public GameObject backgroundPrefab;     // 预制背景
    public GameObject boxPrefab;            // 预制箱子
    public GameObject destinationPrefab;    // 预制目的地
    public GameObject manPrefab;            // 预制小人
    public GameObject wallPrefab;           // 预制墙

    public int Level { get; private set; }              // 关卡数
    public static Vector2 Pivot { get; private set; }   // 中心点
    private List<GameObject> mBaseGameObjects;          // 所有游戏物体
    private int mBoxCount;                              // 未到达目的地的箱子数
    public GameOverEvent mGameOverEvent;                // 游戏结束事件
    public GotoLevelEvent mGotoLevelEvent;              // 进入关卡事件
    private Man mMan;                                   // 小人
    public ManMovedEvent mManMovedEvent;                // 小人移动事件
    private int mMoveCount;                             // 移动次数
    private LinkedList<MoveNode> mMoveList;             // 所有移动结点
    private Status mStatus;                             // 状态

    /**
     * 进入关卡。
     */
    public void EnterLevel() {
        // 删除所有物体
        foreach (GameObject g in mBaseGameObjects) {
            Destroy(g);
        }
        mBaseGameObjects.Clear();

        // 删除所有移动结点
        mMoveList.Clear();

        // 读取关卡文件
        string levelFilePath = string.Format(Const.LEVEL_FILE_PATH, Level);
        TextAsset textAsset = Resources.Load<TextAsset>(levelFilePath);
        char[] text = Resources.Load<TextAsset>(levelFilePath).text.Replace("\r\n", "").ToCharArray();

        // 计算游戏区域左上角、右下角坐标
        float xEndPos = Const.GAME_BOARD_GRID_COUNT_H / 2 - Pivot.x;
        float xStartPos = -xEndPos;
        float yStartPos = Const.GAME_BOARD_GRID_COUNT_H / 2 - Pivot.y;
        float yEndPos = -yStartPos;

        // 创建所有物体
        char read = '0';        // 读取到的字符
        int index = 0;          // 索引
        GameObject go = null;   // 游戏物体
        Box box;                // 箱子
        BaseGameObject.Type type = BaseGameObject.Type.NOTHING; // 类型
        mBoxCount = 0;              // 箱子数置零
        for (float y = yStartPos; y >= yEndPos; y--) {
            for (float x = xStartPos; x <= xEndPos; x++) {
                read = text[index++];
                if (read == '0') {
                    continue;
                }

                type = (BaseGameObject.Type)(read - '0');

                switch (type) {
                    // 创建背景
                    case BaseGameObject.Type.BACKGROUND:
                        go = Instantiate(backgroundPrefab, new Vector3(x, y, -1), Quaternion.identity);
                        mBaseGameObjects.Add(go);
                        break;
                    // 创建箱子
                    case BaseGameObject.Type.BOX:
                        go = Instantiate(backgroundPrefab, new Vector3(x, y, -1), Quaternion.identity);
                        mBaseGameObjects.Add(go);
                        go = Instantiate(boxPrefab, new Vector3(x, y, -2), Quaternion.identity);
                        box = go.GetComponent<Box>();
                        mBoxCount++;
                        break;
                    // 创建目的地
                    case BaseGameObject.Type.DESTINATION:
                        go = Instantiate(destinationPrefab, new Vector3(x, y, -1), Quaternion.identity);
                        mBaseGameObjects.Add(go);
                        break;
                    // 创建小人
                    case BaseGameObject.Type.MAN:
                        go = Instantiate(backgroundPrefab, new Vector3(x, y, -1), Quaternion.identity);
                        mBaseGameObjects.Add(go);
                        go = Instantiate(manPrefab, new Vector3(x, y, -2), Quaternion.identity);
                        mMan = go.GetComponent<Man>();
                        break;
                    // 创建墙
                    case BaseGameObject.Type.WALL:
                        go = Instantiate(wallPrefab, new Vector3(x, y, -1), Quaternion.identity);
                        mBaseGameObjects.Add(go);
                        break;
                }
                mBaseGameObjects.Add(go);
            }
        }

        // 重置成员变量
        mMoveCount = 0;
        mStatus = Status.PLAYING;

        // 发送通知
        mGotoLevelEvent.Invoke(Level);
    }
    
    /**
    * 游戏结束。
    * @param allOver   全部通关则为 true，否则为 false
    */
    private void GameOver(bool allOver) {
        // 将状态设置为已结束
        mStatus = Status.END;

        // 发送通知
        mGameOverEvent.Invoke(allOver);
    }

    /**
    * 进入指定的关卡。
    * @param level 关卡数
    */
    public void GotoLevel(int level) {
        if (level <= Const.LEVEL_COUNT) {
            Level = level;
            this.EnterLevel();
        }
    }

    /**
    * 进入下一关。
    */
    public void GotoNextLevel() {
        if (Level < Const.LEVEL_COUNT) {
            Level++;
            this.EnterLevel();
        }
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private bool IsWon() {
        return mBoxCount == 0;
    }

    /**
     * 开始。 
     */
    private void Start() {
        // 初始化成员
        Level = 1;
        RectTransform rectTransform = gameObject.GetComponent<RectTransform>();
        if (rectTransform != null) {
            Pivot = rectTransform.pivot;
        }
        mBoxCount = 0;
        mBaseGameObjects = new List<GameObject>();
        mGameOverEvent = new GameOverEvent();
        mGotoLevelEvent = new GotoLevelEvent();
        mManMovedEvent = new ManMovedEvent();
        mMoveCount = 0;
        mMoveList = new LinkedList<MoveNode>();
        mStatus = Status.END;

        // 进入第1关
        this.EnterLevel();
    }

    /**
     * 更新。 
     */
    private void Update() {
        // 如果没在玩，则直接返回
        if (mStatus != Status.PLAYING) {
            return;
        }

        // 按键检测
        KeyCode key = KeyCode.None;
        if (Input.GetKeyDown(KeyCode.LeftArrow)) {
            key = KeyCode.LeftArrow;
        } else if (Input.GetKeyDown(KeyCode.RightArrow)) {
            key = KeyCode.RightArrow;
        } else if (Input.GetKeyDown(KeyCode.UpArrow)) {
            key = KeyCode.UpArrow;
        } else if (Input.GetKeyDown(KeyCode.DownArrow)) {
            key = KeyCode.DownArrow;
        }

        // 如果按的不是方向键，则直接返回
        BaseGameObject.MoveResult moveResult = BaseGameObject.MoveResult.CANNOT_MOVE;
        if (key < KeyCode.UpArrow || key > KeyCode.LeftArrow) {
            return;
        }

        // 移动小人。如果不能移动，则直接返回
        BaseGameObject movedBox = mMan.Move(key, ref moveResult);
        if (moveResult <= BaseGameObject.MoveResult.NO_MOVE) {
            return;
        }

        // 增加移动次数
        mMoveCount++;

        // 发送通知
        mManMovedEvent.Invoke(mMoveCount);

        // 判断是否胜利
        if (moveResult == BaseGameObject.MoveResult.ARRIVED_ON_DEST) {
            mBoxCount--;
            if (this.IsWon()) {
                bool allOver = Level == Const.LEVEL_COUNT;
                this.GameOver(allOver);
                return;
            }
        } else if (moveResult == BaseGameObject.MoveResult.LEFT_FROM_DEST) {
            mBoxCount++;
        }

        // 将本次移动加入列表
        mMoveList.AddLast(new MoveNode(movedBox, key));
    }

    /**
    * 重玩本关。
    */
    public void Restart() {
        this.EnterLevel();
    }

    /**
     * 回退，即撤消上一次操作。
     */
    public void Undo() {
        if (mMoveCount == 0) {
            return;
        }

        // 计算方向（方向与最后一次移动的方向相反）
        KeyCode direction = 0;
        MoveNode moveListNode = mMoveList.Last.Value;
        switch (moveListNode.Direction) {
            case KeyCode.DownArrow:
                direction = KeyCode.UpArrow;
                break;
            case KeyCode.LeftArrow:
                direction = KeyCode.RightArrow;
                break;
            case KeyCode.RightArrow:
                direction = KeyCode.LeftArrow;
                break;
            case KeyCode.UpArrow:
                direction = KeyCode.DownArrow;
                break;
        }

        // 移动小人
        BaseGameObject.MoveResult moveResult = BaseGameObject.MoveResult.CANNOT_MOVE;
        mMan.Move(direction, ref moveResult);

        // 移动箱子
        if (moveListNode.Box != null) {
            BaseGameObject box = moveListNode.Box;
            box.Move(direction, ref moveResult);
            if (moveResult == BaseGameObject.MoveResult.LEFT_FROM_DEST) {
                mBoxCount++;
            } else if (moveResult == BaseGameObject.MoveResult.ARRIVED_ON_DEST) {
                mBoxCount--;
            }
        }

        // 删除当前的移动结点
        mMoveList.RemoveLast();

        // 发送通知
        mMoveCount--;
        mManMovedEvent.Invoke(mMoveCount);
    }
}
